You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

163 lines
3.9 KiB
JavaScript

import { Auth } from "./auth.js"
import { Frontend } from "./frontend.js"
import { Storage } from "./storage.js"
export class Server {
async getEnv(variables) {
return Object.fromEntries(
await Promise.all(
variables.map(async variable => {
const {state} = await Deno.permissions.query({
name: 'env', variable
})
if (state === 'granted') {
return [variable, Deno.env.get(variable)]
} else {
return [variable, undefined]
}
})
)
)
}
async configure() {
const env = await this.getEnv([
'PORT',
'BASE_URL',
'REMOTE_BASE_URL',
'GITEA_APP_BASE_URL',
'GITEA_API_BASE_URL',
'GITEA_WEB_BASE_URL',
'GITEA_CLIENT_ID',
'GITEA_CLIENT_SECRET',
])
this.port = env.PORT ?? 3000
this.basePath = env.BASE_PATH ?? '/macchiato'
this.remoteBaseUrl = env.REMOTE_BASE_URL
this.giteaAppBaseUrl = (
env.GITEA_APP_BASE_URL ?? 'http://gitea:3000'
)
this.giteaApiBaseUrl = (
env.GITEA_API_BASE_URL ?? 'http://gitea:3000/api/v1'
)
this.giteaWebBaseUrl = env.GITEA_WEB_BASE_URL
this.giteaClientId = env.GITEA_CLIENT_ID
this.giteaClientSecret = env.GITEA_CLIENT_SECRET
}
async init() {
if (this.port === undefined) {
await this.configure()
}
this.auth = new Auth({
basePath: this.basePath,
remoteBaseUrl: this.remoteBaseUrl,
giteaAppBaseUrl: this.giteaAppBaseUrl,
giteaApiBaseUrl: this.giteaApiBaseUrl,
giteaWebBaseUrl: this.giteaWebBaseUrl,
giteaClientId: this.giteaClientId,
giteaClientSecret: this.giteaClientSecret,
})
this.frontend = new Frontend({
appBaseUrl: this.giteaAppBaseUrl,
apiBaseUrl: this.giteaApiBaseUrl,
})
this.storage = new Storage()
await Promise.all([
...([
'loader',
'editor',
'forms',
'menu',
'settings',
'dialog',
'storage',
'editor-lib-codemirror',
].map(repo => (
server.frontend.loadRepo(
'macchiato',
repo.split('#')[0],
{
srcPath: [],
destPath: [repo],
...(
repo.includes('#') ?
{ref: repo.split('#')[1]} :
{}
),
}
)
))),
server.frontend.loadRepo(
'macchiato',
'pages',
{
srcPath: [],
destPath: [],
ref: 'shared-server',
},
),
server.frontend.loadRepo(
'macchiato',
'server',
{
srcPath: [],
destPath: ['server'],
ref: 'shared-server',
}
),
])
this.frontend.files['/app.js'] = (
this.frontend.files['/server/app.js']
)
this.frontend.files['/'] = (
this.frontend.files['/index.html']
)
const sw = this.frontend.files['/sw.js']
const commit = (
this.frontend.files['/server/app.js'].commit
)
sw.body = (
new TextEncoder().encode(
new TextDecoder().decode(sw.body) +
`\n// ${commit}\n`
)
)
}
async serveRequest(event) {
const {pathname: p} = new URL(event.request.url)
const base = this.basePath
if (p.startsWith(`${base}/api/auth`)) {
await this.auth.serve(event)
} else if (p.startsWith(`${base}/api/storage`)) {
const {allow, headers} = (
await this.auth.requireAuth(event)
)
if (allow) {
await this.storage.serve(event, headers)
}
} else {
await this.frontend.serve(event)
}
}
async serveConn(conn) {
const httpConn = Deno.serveHttp(conn)
for await (const event of httpConn) {
this.serveRequest(event)
}
}
async serve() {
if (!this.server) {
await this.init()
}
this.server = Deno.listen({
port: this.port,
})
for await (const conn of this.server) {
this.serveConn(conn)
}
}
}