Skip to content

Commit 66e8a46

Browse files
authored
Merge pull request #988 from fosrl/dev
1.6.0
2 parents 2f5579b + 862dd6f commit 66e8a46

File tree

217 files changed

+15379
-4380
lines changed

Some content is hidden

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

217 files changed

+15379
-4380
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ install/
2626
bruno/
2727
LICENSE
2828
CONTRIBUTING.md
29+
dist

.github/workflows/linting.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: ESLint
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- '**/*.js'
7+
- '**/*.jsx'
8+
- '**/*.ts'
9+
- '**/*.tsx'
10+
- '.eslintrc*'
11+
- 'package.json'
12+
- 'yarn.lock'
13+
- 'pnpm-lock.yaml'
14+
- 'package-lock.json'
15+
16+
jobs:
17+
Linter:
18+
runs-on: ubuntu-latest
19+
steps:
20+
- name: Checkout code
21+
uses: actions/checkout@v4
22+
23+
- name: Set up Node.js
24+
uses: actions/setup-node@v4
25+
with:
26+
node-version: '20'
27+
28+
- name: Install dependencies
29+
run: |
30+
npm ci
31+
32+
- name: Run ESLint
33+
run: |
34+
npx eslint . --ext .js,.jsx,.ts,.tsx

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,5 @@ installer
3232
bin
3333
.secrets
3434
test_event.json
35-
.idea/
35+
.idea/
36+
server/db/index.ts

Dockerfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ RUN echo 'export * from "./sqlite";' > server/db/index.ts
1313
RUN npx drizzle-kit generate --dialect sqlite --schema ./server/db/sqlite/schema.ts --out init
1414

1515
RUN npm run build:sqlite
16+
RUN npm run build:cli
1617

1718
FROM node:20-alpine AS runner
1819

@@ -30,6 +31,9 @@ COPY --from=builder /app/.next/static ./.next/static
3031
COPY --from=builder /app/dist ./dist
3132
COPY --from=builder /app/init ./dist/init
3233

34+
COPY ./cli/wrapper.sh /usr/local/bin/pangctl
35+
RUN chmod +x /usr/local/bin/pangctl ./dist/cli.mjs
36+
3337
COPY server/db/names.json ./dist/names.json
3438

3539
COPY public ./public

Dockerfile.pg

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ RUN echo 'export * from "./pg";' > server/db/index.ts
1313
RUN npx drizzle-kit generate --dialect postgresql --schema ./server/db/pg/schema.ts --out init
1414

1515
RUN npm run build:pg
16+
RUN npm run build:cli
1617

1718
FROM node:20-alpine AS runner
1819

@@ -30,6 +31,9 @@ COPY --from=builder /app/.next/static ./.next/static
3031
COPY --from=builder /app/dist ./dist
3132
COPY --from=builder /app/init ./dist/init
3233

34+
COPY ./cli/wrapper.sh /usr/local/bin/pangctl
35+
RUN chmod +x /usr/local/bin/pangctl ./dist/cli.mjs
36+
3337
COPY server/db/names.json ./dist/names.json
3438

3539
COPY public ./public
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import { CommandModule } from "yargs";
2+
import { hashPassword, verifyPassword } from "@server/auth/password";
3+
import { db, resourceSessions, sessions } from "@server/db";
4+
import { users } from "@server/db";
5+
import { eq, inArray } from "drizzle-orm";
6+
import moment from "moment";
7+
import { fromError } from "zod-validation-error";
8+
import { passwordSchema } from "@server/auth/passwordSchema";
9+
import { UserType } from "@server/types/UserTypes";
10+
import { generateRandomString, RandomReader } from "@oslojs/crypto/random";
11+
12+
type SetAdminCredentialsArgs = {
13+
email: string;
14+
password: string;
15+
};
16+
17+
export const setAdminCredentials: CommandModule<{}, SetAdminCredentialsArgs> = {
18+
command: "set-admin-credentials",
19+
describe: "Set the server admin credentials",
20+
builder: (yargs) => {
21+
return yargs
22+
.option("email", {
23+
type: "string",
24+
demandOption: true,
25+
describe: "Admin email address"
26+
})
27+
.option("password", {
28+
type: "string",
29+
demandOption: true,
30+
describe: "Admin password"
31+
});
32+
},
33+
handler: async (argv: { email: string; password: string }) => {
34+
try {
35+
const { email, password } = argv;
36+
37+
const parsed = passwordSchema.safeParse(password);
38+
39+
if (!parsed.success) {
40+
throw Error(
41+
`Invalid server admin password: ${fromError(parsed.error).toString()}`
42+
);
43+
}
44+
45+
const passwordHash = await hashPassword(password);
46+
47+
await db.transaction(async (trx) => {
48+
try {
49+
const [existing] = await trx
50+
.select()
51+
.from(users)
52+
.where(eq(users.serverAdmin, true));
53+
54+
if (existing) {
55+
const passwordChanged = !(await verifyPassword(
56+
password,
57+
existing.passwordHash!
58+
));
59+
60+
if (passwordChanged) {
61+
await trx
62+
.update(users)
63+
.set({ passwordHash })
64+
.where(eq(users.userId, existing.userId));
65+
66+
await invalidateAllSessions(existing.userId);
67+
console.log("Server admin password updated");
68+
}
69+
70+
if (existing.email !== email) {
71+
await trx
72+
.update(users)
73+
.set({ email, username: email })
74+
.where(eq(users.userId, existing.userId));
75+
76+
console.log("Server admin email updated");
77+
}
78+
} else {
79+
const userId = generateId(15);
80+
81+
await trx.update(users).set({ serverAdmin: false });
82+
83+
await db.insert(users).values({
84+
userId: userId,
85+
email: email,
86+
type: UserType.Internal,
87+
username: email,
88+
passwordHash,
89+
dateCreated: moment().toISOString(),
90+
serverAdmin: true,
91+
emailVerified: true
92+
});
93+
94+
console.log("Server admin created");
95+
}
96+
} catch (e) {
97+
console.error("Failed to set admin credentials", e);
98+
trx.rollback();
99+
throw e;
100+
}
101+
});
102+
103+
console.log("Admin credentials updated successfully");
104+
process.exit(0);
105+
} catch (error) {
106+
console.error("Error:", error);
107+
process.exit(1);
108+
}
109+
}
110+
};
111+
112+
export async function invalidateAllSessions(userId: string): Promise<void> {
113+
try {
114+
await db.transaction(async (trx) => {
115+
const userSessions = await trx
116+
.select()
117+
.from(sessions)
118+
.where(eq(sessions.userId, userId));
119+
await trx.delete(resourceSessions).where(
120+
inArray(
121+
resourceSessions.userSessionId,
122+
userSessions.map((s) => s.sessionId)
123+
)
124+
);
125+
await trx.delete(sessions).where(eq(sessions.userId, userId));
126+
});
127+
} catch (e) {
128+
console.log("Failed to all invalidate user sessions", e);
129+
}
130+
}
131+
132+
const random: RandomReader = {
133+
read(bytes: Uint8Array): void {
134+
crypto.getRandomValues(bytes);
135+
}
136+
};
137+
138+
export function generateId(length: number): string {
139+
const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
140+
return generateRandomString(random, alphabet, length);
141+
}

cli/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env node
2+
3+
import yargs from "yargs";
4+
import { hideBin } from "yargs/helpers";
5+
import { setAdminCredentials } from "@cli/commands/setAdminCredentials";
6+
7+
yargs(hideBin(process.argv))
8+
.scriptName("pangctl")
9+
.command(setAdminCredentials)
10+
.demandCommand()
11+
.help().argv;

cli/wrapper.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
cd /app/
3+
./dist/cli.mjs "$@"

config/config.example.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,6 @@ rate_limits:
4141
window_minutes: 1
4242
max_requests: 500
4343

44-
users:
45-
server_admin:
46-
email: "admin@example.com"
47-
password: "Password123!"
48-
4944
flags:
5045
require_email_verification: false
5146
disable_signup_without_invite: true

crowdin.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
files:
2+
- source: /messages/en-US.json
3+
translation: /messages/%locale%.json

0 commit comments

Comments
 (0)