Skip to content

Commit f759371

Browse files
authored
Merge pull request umami-software#3392 from umami-software/dev
v2.18.0
2 parents 38ab685 + 64505bb commit f759371

File tree

214 files changed

+58648
-12243
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

214 files changed

+58648
-12243
lines changed

Dockerfile

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,49 +3,50 @@ FROM node:22-alpine AS deps
33
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
44
RUN apk add --no-cache libc6-compat
55
WORKDIR /app
6-
COPY package.json yarn.lock ./
7-
# Add yarn timeout to handle slow CPU when Github Actions
8-
RUN yarn config set network-timeout 300000
9-
RUN yarn install --frozen-lockfile
6+
COPY package.json pnpm-lock.yaml ./
7+
RUN npm install -g pnpm
8+
RUN pnpm install --frozen-lockfile
109

1110
# Rebuild the source code only when needed
1211
FROM node:22-alpine AS builder
1312
WORKDIR /app
1413
COPY --from=deps /app/node_modules ./node_modules
1514
COPY . .
16-
COPY docker/middleware.js ./src
1715

1816
ARG DATABASE_TYPE
1917
ARG BASE_PATH
2018

21-
ENV DATABASE_TYPE $DATABASE_TYPE
22-
ENV BASE_PATH $BASE_PATH
19+
ENV DATABASE_TYPE=$DATABASE_TYPE
20+
ENV BASE_PATH=$BASE_PATH
2321

24-
ENV NEXT_TELEMETRY_DISABLED 1
22+
ENV NEXT_TELEMETRY_DISABLED=1
2523

26-
RUN yarn build-docker
24+
RUN npm run build-docker
2725

2826
# Production image, copy all the files and run next
2927
FROM node:22-alpine AS runner
3028
WORKDIR /app
3129

3230
ARG NODE_OPTIONS
3331

34-
ENV NODE_ENV production
35-
ENV NEXT_TELEMETRY_DISABLED 1
36-
ENV NODE_OPTIONS $NODE_OPTIONS
32+
ENV NODE_ENV=production
33+
ENV NEXT_TELEMETRY_DISABLED=1
34+
ENV NODE_OPTIONS=$NODE_OPTIONS
3735

3836
RUN addgroup --system --gid 1001 nodejs
3937
RUN adduser --system --uid 1001 nextjs
38+
RUN npm install -g pnpm
4039

4140
RUN set -x \
42-
&& apk add --no-cache curl \
43-
&& yarn add npm-run-all dotenv semver [email protected]
41+
&& apk add --no-cache curl
42+
43+
# Script dependencies
44+
RUN pnpm add npm-run-all dotenv [email protected]
45+
46+
# Permissions for prisma
47+
RUN chown -R nextjs:nodejs node_modules/.pnpm/
4448

45-
# You only need to copy next.config.js if you are NOT using the default configuration
46-
COPY --from=builder /app/next.config.js .
4749
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
48-
COPY --from=builder /app/package.json ./package.json
4950
COPY --from=builder /app/prisma ./prisma
5051
COPY --from=builder /app/scripts ./scripts
5152

@@ -54,11 +55,14 @@ COPY --from=builder /app/scripts ./scripts
5455
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
5556
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
5657

58+
# Custom routes
59+
RUN mv ./.next/routes-manifest.json ./.next/routes-manifest-orig.json
60+
5761
USER nextjs
5862

5963
EXPOSE 3000
6064

61-
ENV HOSTNAME 0.0.0.0
62-
ENV PORT 3000
65+
ENV HOSTNAME=0.0.0.0
66+
ENV PORT=3000
6367

64-
CMD ["yarn", "start-docker"]
68+
CMD ["pnpm", "start-docker"]

README.md

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,12 @@ A detailed getting started guide can be found at [umami.is/docs](https://umami.i
3838
- A server with Node.js version 18.18 or newer
3939
- A database. Umami supports [MariaDB](https://www.mariadb.org/) (minimum v10.5), [MySQL](https://www.mysql.com/) (minimum v8.0) and [PostgreSQL](https://www.postgresql.org/) (minimum v12.14) databases.
4040

41-
### Install Yarn
42-
43-
```bash
44-
npm install -g yarn
45-
```
46-
4741
### Get the Source Code and Install Packages
4842

4943
```bash
5044
git clone https://github.com/umami-software/umami.git
5145
cd umami
52-
yarn install
46+
npm install
5347
```
5448

5549
### Configure Umami
@@ -70,15 +64,15 @@ mysql://username:mypassword@localhost:3306/mydb
7064
### Build the Application
7165

7266
```bash
73-
yarn build
67+
npm build
7468
```
7569

7670
*The build step will create tables in your database if you are installing for the first time. It will also create a login user with username **admin** and password **umami**.*
7771

7872
### Start the Application
7973

8074
```bash
81-
yarn start
75+
npm run start
8276
```
8377

8478
*By default, this will launch the application on `http://localhost:3000`. You will need to either [proxy](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) requests from your web server or change the [port](https://nextjs.org/docs/api-reference/cli#production) to serve the application directly.*
@@ -113,8 +107,8 @@ To get the latest features, simply do a pull, install any new dependencies, and
113107

114108
```bash
115109
git pull
116-
yarn install
117-
yarn build
110+
npm install
111+
npm run build
118112
```
119113

120114
To update the Docker image, simply pull the new images and rebuild:

cypress.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ export default defineConfig({
88
env: {
99
umami_user: 'admin',
1010
umami_password: 'umami',
11+
umami_user_id: '41e2b680-648e-4b09-bcd7-3e2b10c06264',
1112
},
1213
});

cypress/e2e/api-team.cy.ts

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
describe('Team API tests', () => {
2+
Cypress.session.clearAllSavedSessions();
3+
4+
let teamId;
5+
let userId;
6+
7+
before(() => {
8+
cy.login(Cypress.env('umami_user'), Cypress.env('umami_password'));
9+
cy.fixture('users').then(data => {
10+
const userCreate = data.userCreate;
11+
cy.request({
12+
method: 'POST',
13+
url: '/api/users',
14+
headers: {
15+
'Content-Type': 'application/json',
16+
Authorization: Cypress.env('authorization'),
17+
},
18+
body: userCreate,
19+
}).then(response => {
20+
userId = response.body.id;
21+
expect(response.status).to.eq(200);
22+
expect(response.body).to.have.property('username', 'cypress1');
23+
expect(response.body).to.have.property('role', 'user');
24+
});
25+
});
26+
});
27+
28+
it('Creates a team.', () => {
29+
cy.fixture('teams').then(data => {
30+
const teamCreate = data.teamCreate;
31+
cy.request({
32+
method: 'POST',
33+
url: '/api/teams',
34+
headers: {
35+
'Content-Type': 'application/json',
36+
Authorization: Cypress.env('authorization'),
37+
},
38+
body: teamCreate,
39+
}).then(response => {
40+
teamId = response.body[0].id;
41+
expect(response.status).to.eq(200);
42+
expect(response.body[0]).to.have.property('name', 'cypress');
43+
expect(response.body[1]).to.have.property('role', 'team-owner');
44+
});
45+
});
46+
});
47+
48+
it('Gets a teams by ID.', () => {
49+
cy.request({
50+
method: 'GET',
51+
url: `/api/teams/${teamId}`,
52+
headers: {
53+
'Content-Type': 'application/json',
54+
Authorization: Cypress.env('authorization'),
55+
},
56+
}).then(response => {
57+
expect(response.status).to.eq(200);
58+
expect(response.body).to.have.property('id', teamId);
59+
});
60+
});
61+
62+
it('Updates a team.', () => {
63+
cy.fixture('teams').then(data => {
64+
const teamUpdate = data.teamUpdate;
65+
cy.request({
66+
method: 'POST',
67+
url: `/api/teams/${teamId}`,
68+
headers: {
69+
'Content-Type': 'application/json',
70+
Authorization: Cypress.env('authorization'),
71+
},
72+
body: teamUpdate,
73+
}).then(response => {
74+
expect(response.status).to.eq(200);
75+
expect(response.body).to.have.property('id', teamId);
76+
expect(response.body).to.have.property('name', 'cypressUpdate');
77+
});
78+
});
79+
});
80+
81+
it('Get all users that belong to a team.', () => {
82+
cy.request({
83+
method: 'GET',
84+
url: `/api/teams/${teamId}/users`,
85+
headers: {
86+
'Content-Type': 'application/json',
87+
Authorization: Cypress.env('authorization'),
88+
},
89+
}).then(response => {
90+
expect(response.status).to.eq(200);
91+
expect(response.body.data[0]).to.have.property('id');
92+
expect(response.body.data[0]).to.have.property('teamId');
93+
expect(response.body.data[0]).to.have.property('userId');
94+
expect(response.body.data[0]).to.have.property('user');
95+
});
96+
});
97+
98+
it('Get a user belonging to a team.', () => {
99+
cy.request({
100+
method: 'GET',
101+
url: `/api/teams/${teamId}/users/${Cypress.env('umami_user_id')}`,
102+
headers: {
103+
'Content-Type': 'application/json',
104+
Authorization: Cypress.env('authorization'),
105+
},
106+
}).then(response => {
107+
expect(response.status).to.eq(200);
108+
expect(response.body).to.have.property('teamId');
109+
expect(response.body).to.have.property('userId');
110+
expect(response.body).to.have.property('role');
111+
});
112+
});
113+
114+
it('Get all websites belonging to a team.', () => {
115+
cy.request({
116+
method: 'GET',
117+
url: `/api/teams/${teamId}/websites`,
118+
headers: {
119+
'Content-Type': 'application/json',
120+
Authorization: Cypress.env('authorization'),
121+
},
122+
}).then(response => {
123+
expect(response.status).to.eq(200);
124+
expect(response.body).to.have.property('data');
125+
});
126+
});
127+
128+
it('Add a user to a team.', () => {
129+
cy.request({
130+
method: 'POST',
131+
url: `/api/teams/${teamId}/users`,
132+
headers: {
133+
'Content-Type': 'application/json',
134+
Authorization: Cypress.env('authorization'),
135+
},
136+
body: {
137+
userId,
138+
role: 'team-member',
139+
},
140+
}).then(response => {
141+
expect(response.status).to.eq(200);
142+
expect(response.body).to.have.property('userId', userId);
143+
expect(response.body).to.have.property('role', 'team-member');
144+
});
145+
});
146+
147+
it(`Update a user's role on a team.`, () => {
148+
cy.request({
149+
method: 'POST',
150+
url: `/api/teams/${teamId}/users/${userId}`,
151+
headers: {
152+
'Content-Type': 'application/json',
153+
Authorization: Cypress.env('authorization'),
154+
},
155+
body: {
156+
role: 'team-view-only',
157+
},
158+
}).then(response => {
159+
expect(response.status).to.eq(200);
160+
expect(response.body).to.have.property('userId', userId);
161+
expect(response.body).to.have.property('role', 'team-view-only');
162+
});
163+
});
164+
165+
it(`Remove a user from a team.`, () => {
166+
cy.request({
167+
method: 'DELETE',
168+
url: `/api/teams/${teamId}/users/${userId}`,
169+
headers: {
170+
'Content-Type': 'application/json',
171+
Authorization: Cypress.env('authorization'),
172+
},
173+
}).then(response => {
174+
expect(response.status).to.eq(200);
175+
});
176+
});
177+
178+
it('Deletes a team.', () => {
179+
cy.request({
180+
method: 'DELETE',
181+
url: `/api/teams/${teamId}`,
182+
headers: {
183+
'Content-Type': 'application/json',
184+
Authorization: Cypress.env('authorization'),
185+
},
186+
}).then(response => {
187+
expect(response.status).to.eq(200);
188+
expect(response.body).to.have.property('ok', true);
189+
});
190+
});
191+
192+
// it('Gets all teams that belong to a user.', () => {
193+
// cy.request({
194+
// method: 'GET',
195+
// url: `/api/users/${userId}/teams`,
196+
// headers: {
197+
// 'Content-Type': 'application/json',
198+
// Authorization: Cypress.env('authorization'),
199+
// },
200+
// }).then(response => {
201+
// expect(response.status).to.eq(200);
202+
// expect(response.body).to.have.property('data');
203+
// });
204+
// });
205+
206+
after(() => {
207+
cy.deleteUser(userId);
208+
});
209+
});

0 commit comments

Comments
 (0)